{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 一、爬虫的流程：\n",
    "\n",
    "1. 打开网页：可用 Requests 访问页面，得到服务器返回的数据，包括 HTML 页面以及 JSON 数据\n",
    "2. 提取数据：针对 HTML 页面，可用 XPath 进行元素定位，提取数据；针对 JSON 数据，可用 JSON 进行解析\n",
    "3. 保存数据：可用 Pandas 保存数据，最后导出 CSV 文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Requests 访问页面\n",
    "import requests\n",
    "# ____有两种访问方式：Get 和 Post，Get 把参数包含在 url 中，而 Post 通过 request body 来传递参数\n",
    "# ____用 Get 访问豆瓣：\n",
    "r = requests.get('http://www.douban.com') # r是Get请求后的结果，可用r.text或r.content来获取HTML正文\n",
    "# ____用 Post 进行表单传递:\n",
    "r = requests.post('http://xxx.com', data = {'key':'value'}) # data 是传递的表单参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# XPath 定位\n",
    "# ____举例：\n",
    "# ______xpath(‘node’) 选取了 node 节点的所有子节点；\n",
    "# ______xpath(’/div’) 从根节点上选取 div 节点；\n",
    "# ______xpath(’//div’) 选取所有的 div 节点；\n",
    "# ______xpath(’./div’) 选取当前节点下的 div 节点；\n",
    "# ______xpath(’…’) 回到上一个节点；\n",
    "# ______xpath(’//@id’) 选取所有的 id 属性；\n",
    "# ______xpath(’//book[@id]’) 选取所有拥有名为 id 的属性的 book 元素；\n",
    "# ______xpath(’//book[@id=“abc”]’) 选取所有 book 元素，且这些 book 元素拥有 id= \"abc\"的属性；\n",
    "# ______xpath(’//book/title | //book/price’) 选取 book 元素的所有 title 和 price 元素\n",
    "# ____定位到 HTML 中的所有列表项目:\n",
    "from lxml import etree\n",
    "html = etree.HTML(html)\n",
    "result = html.xpath('//li')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# JSON对象\n",
    "# JSON库将JSON对象转换成为Python对象，以便对数据进行解析\n",
    "# ____json.dumps()  将Python对象转换成JSON对象\n",
    "# ____json.loads()  将JSON对象转换成Python对象\n",
    "import json\n",
    "jsonData = '{\"a\":1,\"b\":2,\"c\":3,\"d\":4,\"e\":5}';\n",
    "input = json.loads(jsonData)\n",
    "print (input)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 二、实战一：如何使用 JSON 数据自动下载王祖贤的海报\n",
    "\n",
    "- 打开网页；输入关键词“王祖贤”；在搜索结果页中选择“图片”；下载图片页中的所有海报"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 注意事项：\n",
    "# ____如果爬取的页面是动态页面，就需要关注 XHR 数据，因为动态页面的原理就是通过原生的 XHR 数据对象发出 HTTP 请求，得到服务器返回的数据后，再进行处理。XHR 会用于在后台与服务器交换数据\n",
    "# ____豆瓣搜索“王祖贤”，点击图片，发现 XHR 数据中有请求是这样的：https://www.douban.com/j/search_photo?q=%E7%8E%8B%E7%A5%96%E8%B4%A4&limit=20&start=0\n",
    "# ____打开链接可以看到请求数据\"total\":24957,\"limit\":20，王祖贤的图片一共有 22471 张，其中一次只返回了 20 张\n",
    "# ____数据被放到了 images 对象里，它是个数组的结构，每个数组的元素是个字典的类型，分别告诉了 src、author、url、id、title、width 和 height 字段，这些字段代表的含义分别是原图片的地址、作者、发布地址、图片 ID、标题、图片宽度、图片高度等信息\n",
    "# ____本身网址：https://www.douban.com/j/search_photo?q=%E7%8E%8B%E7%A5%96%E8%B4%A4&limit=20&start=0\n",
    "# ____start 实际上是请求的起始 ID，它对图片的顺序标识是从 0 开始计算的。所以想要从第 21 个图片进行下载，可将 start 设置为 20。\n",
    "# coding:utf-8\n",
    "import requests\n",
    "import json\n",
    "query = '王祖贤'\n",
    "''' 下载图片 '''\n",
    "def download(src, id):\n",
    "  dir = './' + str(id) + '.jpg'\n",
    "  try:\n",
    "    pic = requests.get(src, timeout=10)\n",
    "    fp = open(dir, 'wb')\n",
    "    fp.write(pic.content)\n",
    "    fp.close()\n",
    "  except requests.exceptions.ConnectionError:\n",
    "    print('图片无法下载')\n",
    "            \n",
    "''' for 循环 请求全部的 url '''\n",
    "for i in range(0, 5, 20):  # for i in range(0, 24957, 20):\n",
    "  url = 'https://www.douban.com/j/search_photo?q='+query+'&limit=20&start='+str(i)\n",
    "  html = requests.get(url).text    # 得到返回结果\n",
    "  response = json.loads(html,encoding='utf-8') # 将 JSON 格式转换成 Python 对象\n",
    "  for image in response['images']:\n",
    "    print(image['src']) # 查看当前下载的图片网址\n",
    "    download(image['src'], image['id']) # 下载一张图片"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 三、实战二：如何使用 XPath 自动下载王祖贤的电影海报封面\n",
    "\n",
    "- 如果遇到 JSON 的数据格式，直接通过上面介绍的 Python 的 JSON 库就可以解析。但有时候，网页会用 JS 请求数据，那么只有 JS 都加载完之后，才能获取完整的 HTML 文件。XPath 可以不受加载的限制，帮我们定位想要的元素"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 1、从豆瓣电影上下载王祖贤的电影封面:\n",
    "\n",
    "- 打开网页 movie.douban.com；输入关键词“王祖贤”；下载图片页中的所有电影封面"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# XPath Helper 插件，使用 Ctrl+Shift+X 快捷键，并用鼠标选中你想要定位的元素，会得到对应元素位置的XPath\n",
    "# QUERY：\n",
    "/html/body/div[@id='wrapper']/div[@id='root']/div[@class='sc-gqjmRU jzSYhi']/div[@class='_fbyzagxnq']/div[@class='_eefy02cnt']/div[1]/div[@class='sc-bxivhb gemzcp']/div[@class='item-root']/a[@class='cover-link']/img[@class='cover']/@src\n",
    "# RESULTS (1)：\n",
    "https://img1.doubanio.com/view/celebrity/s_ratio_celebrity/public/p5618.webp\n",
    "# 这里只选择一个元素，要匹配上所有的电影海报，就需要缩减 XPath 表达式。在Query中进行XPath缩减，尝试去掉XPath表达式中的一些内容，在Results中会自动出现匹配的结果\n",
    "# 缩减之后，得到电影海报的 XPath（假设为变量 src_xpath）：\n",
    "//div[@class='item-root']/a[@class='cover-link']/img[@class='cover']/@src\n",
    "# 电影名称的 XPath（假设为变量 title_xpath）：\n",
    "//div[@class='item-root']/div[@class='detail']/div[@class='title']/a[@class='title-text']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 注意事项：\n",
    "# ____有时候直接用 Requests 获取 HTML 时，发现想要的 XPath 并不存在。这是因为 HTML 还没有加载完，因此你需要一个工具，来进行网页加载的模拟，直到完成加载后再给你完整的 HTML\n",
    "# ____这个工具就是 Selenium 库，它是 Web 应用的测试工具，在浏览器中模拟用户在进行操作，使用方法如下：\n",
    "from selenium import webdriver\n",
    "driver = webdriver.Chrome()\n",
    "driver.get(request_url)\n",
    "# 当获取到完整的 HTML 时，就可以对 HTML 中的 XPath 进行提取，在这里我们需要找到图片地址 srcs 和电影名称 titles。\n",
    "# 这里通过 XPath 语法匹配到了多个元素，因为是多个元素，所以我们需要用 for 循环来对每个元素进行提取\n",
    "srcs = html.xpath(src_xpath)\n",
    "titles = html.xpath(title_path)\n",
    "for src, title in zip(srcs, titles):\n",
    "    download(src, title.text)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 完整爬虫：(还未运行成功，待定)\n",
    "from lxml import etree\n",
    "from selenium import webdriver\n",
    "# driver = webdriver.Chrome()\n",
    "# driver.get(request_url)\n",
    "request_url = 'https://movie.douban.com/subject_search?search_text=王祖贤&cat=1002'\n",
    "src_xpath = \"//div[@class='item-root']/a[@class='cover-link']/img[@class='cover']/@src\"\n",
    "title_xpath = \"//div[@class='item-root']/div[@class='detail']/div[@class='title']/a[@class='title-text']\"\n",
    "driver = webdriver.Chrome('/Users/apple/Downloads/chromedriver')\n",
    "driver.get(request_url)\n",
    "html = etree.HTML(driver.page_source)\n",
    "print(html)\n",
    "srcs = html.xpath(src_xpath)\n",
    "print(srcs)\n",
    "titles = html.xpath(title_xpath)\n",
    "for src, title in zip(srcs, titles):\n",
    "    download(src, title.text)\n",
    "    print (src)\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
